<template>
  <div class="split-container">
    <div ref="leftPane" class="split-pane">
      <!-- 左侧内容 -->
      <el-card class="split-pane-card">
        <el-collapse v-model="activeNames">
          <el-collapse-item title="活动节点" name="1">
            <el-button @dblclick="addNodeByType(item.type)" :type="item.type" v-for="(item, key) in sourceNodes"
              :key="key" :icon="item.icon" :style="{ margin: '2px' }">{{ item.title }}</el-button>
          </el-collapse-item>
        </el-collapse>
      </el-card>
    </div>
    <div ref="middlePane" class="split-pane">
      <!-- 中间内容 -->
      <el-card class="split-pane-card">
        <div id="jsplumb-container" class="jsplumb-container">
          <el-button v-for="(item, key) in flowNodeValue" :type="item.type" :key="key" :icon="item.icon" :id="item.key"
            class="wf-btn" @mousedown="setNode(item)">
            {{ item.title }}
          </el-button>
        </div>
      </el-card>
    </div>
    <div ref="rightPane" class="split-pane">
      <!-- 右侧内容 -->
      <el-card class="split-pane-card">
        <el-tabs v-model="tabValue" class="split-pane-tabs">
          <el-tab-pane label="连线" name="connection" :disabled="!isclickLine">
            <el-form label-position="top" :model="conditionNode">
              <el-form-item label="连线标题">
                <el-input v-model="conditionNode.label" @on-keyup="revalidateConnection"></el-input>
              </el-form-item>
              <el-form-item label="条件">
                <el-row :gutter="5" v-for="(item, index) in conditionNode.conditions" :key="index">
                  <el-col :span="6">
                    <el-select size="small" v-model="item.field">
                      <el-option v-for="workflowInput in workflowInputs" :key="workflowInput.name"
                        :label="workflowInput.name" :value="workflowInput.name">
                        {{ workflowInput.name }}
                      </el-option>
                    </el-select>
                  </el-col>
                  <el-col :span="6">
                    <el-select size="small" v-model="item.operator">
                      <el-option label="大于" value=">"></el-option>
                      <el-option label="大于等于" value=">="></el-option>
                      <el-option label="小于" value="<"></el-option>
                      <el-option label="小于等于" value="<="></el-option>
                      <el-option label="不等于" value="!="></el-option>
                      <el-option label="等于" value="=="></el-option>
                    </el-select>
                  </el-col>
                  <el-col :span="6">
                    <el-input size="small" v-model="item.value" :minlength="1"></el-input>
                  </el-col>
                  <el-col :span="6">
                    <el-button size="small" type="danger" @click="removeCondition(index)">删除</el-button>
                  </el-col>
                </el-row>
              </el-form-item>
              <el-form-item>
                <el-button style="border-style: dashed; width: 100%" @click="addCondition" icon="Plus">添加条件</el-button>
              </el-form-item>
              <el-form-item>
                <el-button class="delet-btn" @click="removeConnection" icon="Delete">删除连线</el-button>
              </el-form-item>
            </el-form>
          </el-tab-pane>
          <el-tab-pane label="节点" name="node" :disabled="isclickLine">
            <el-form label-position="top" :model="currentNode">
              <el-form-item label="标识">
                <el-input v-model="currentNode.key" :disabled="true"></el-input>
              </el-form-item>
              <el-form-item label="标题">
                <el-input v-model="currentNode.title" :minlength="1" @keyup="revalidate"></el-input>
              </el-form-item>
              <el-form-item label="执行操作">
                <el-select v-model="tempStepBodyName" @change="onStepBodyChange">
                  <el-option v-for="(item, key) in stepBodys" :value="item.name" :label="item.displayName"
                    :key="key"></el-option>
                </el-select>
              </el-form-item>
              <el-form-item v-for="(item, key) in currentNode.stepBody.inputs" :label="item.displayName" :key="key">
                <el-input type="text" v-if="item.inputType.name == 'SINGLE_LINE_STRING' && tempStepBodyName"
                  v-model="item.value"></el-input>
                <el-checkbox v-model="item.value" v-if="item.inputType.name == 'CHECKBOX' && tempStepBodyName"
                  :label="item.displayName" :value="item.value">
                  {{ item.displayName }}
                </el-checkbox>
                <UserDropdown v-model="item.value" v-if="item.inputType.name == 'SELECT_USERS' && tempStepBodyName">
                </UserDropdown>
                <RoleDropdown v-model="item.value" v-if="item.inputType.name == 'SELECT_ROLES' && tempStepBodyName">
                </RoleDropdown>
              </el-form-item>
              <el-form-item>
                <el-button class="delet-btn" @click="removeNode" icon="Delete">删除节点</el-button>
              </el-form-item>
            </el-form>
          </el-tab-pane>
        </el-tabs>
      </el-card>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed, nextTick, onBeforeUnmount, watch } from 'vue';
// 导入split.js
//@ts-ignore
import Split from 'split.js';
// 导入pinia仓库
import pinia from '@/stores';
// 导入工作流小仓储
import useWorkflowStore from '@/stores/modules/workflow';
// 导入节点和输入类型
import type {
  ConditionFlowNode,
  FlowNode,
  WorkflowInput
} from '@/utils/api/workflow/definition/type';
// 导入jsplumb实例
import { jsPlumb, jsPlumbInstance, type Connection, type ConnectionMadeEventInfo, type DragEventCallbackOptions } from 'jsplumb';
// 导入Message
import { ElMessage, ElMessageBox } from 'element-plus';
import util from '@/utils/tools/util';
import { RoleDropdown, UserDropdown } from '@/components/dropdown/index';

// 获取工作流小仓储实例
const workflowStore = useWorkflowStore(pinia);
// 左边容器实例
const leftPane = ref<HTMLDivElement>();
// 中间容器实例
const middlePane = ref<HTMLDivElement>();
// 右边容器实例
const rightPane = ref<HTMLDivElement>();
// 分割视图实例
let splitInstance: Split.Instance;
//折叠板默认展开name
const activeNames = ref(['1']);

// 组件挂载
onMounted(async () => {
  // 初始化分视图
  initSplit();

  await workflowStore.init();

  // 初始化jsplumb
  await nextTick(() => {
    plumbIns.value.ready(() => {
      plumbIns.value.setContainer('jsplumb-container');
      plumbIns.value.bind('connection', onConnection);
      plumbIns.value.bind('click', onClickConnection);
      if (flowNodeValue.value.length > 0) {
        initWorkflow();
      }
    });
  });
});

// 组件卸载
onBeforeUnmount(() => {
  if (splitInstance) {
    splitInstance.destroy();
  }
});

// 初始化split
const initSplit = () => {
  splitInstance = Split([leftPane.value!, middlePane.value!, rightPane.value!], {
    sizes: [20, 60, 20], // 初始比例
    minSize: [200, 200, 200], // 最小尺寸限制
    gutterSize: 8, // 分割线大小
    direction: 'horizontal', // 水平分割
    cursor: 'col-resize', // 光标样式
    onDragEnd: () => {
      // 拖动结束时的回调，可以在此处更新状态或发送事件
    }
  });
};

// 定义 emits 用于向父组件传递更新事件
const emits = defineEmits<{
  (e: 'update:modelValue', data: Array<any>): void;
}>();

// 使用 computed 从 props 创建一个可读写的本地副本，这样可以直接修改它，同时触发更新事件向父组件回传数据(即实现父子组件的数据双向绑定)
const flowNodeValue = computed({
  get(): Array<FlowNode> {
    return props.modelValue;
  },
  set(newValue: Array<FlowNode>) {
    emits('update:modelValue', newValue);
  }
});

// props参数
const props = defineProps({
  modelValue: {
    type: Array<FlowNode>,
    required: true
  },
  inputs: {
    type: Array<Array<Array<WorkflowInput>>>,
    required: true
  }
});

// 获取节点
const getNode = (key: string) => {
  return flowNodeValue.value.filter((u) => u.key == key)[0];
};

// 节点流程
const sourceNodes = computed(() => {
  return workflowStore.getNodes;
});

// 获取输入条件
const workflowInputs = computed(() => {
  let arr: any = [];
  props.inputs.forEach((element) => {
    element.forEach((obj) => {
      arr.push(...obj);
    });
  });
  return arr;
});

// 组件实例
const plumbIns = ref<jsPlumbInstance>(jsPlumb.getInstance(workflowStore.getJsPlumbOptions));

// 是否点击连线
const isclickLine = ref<boolean>(false);
// 当前节点
const currentNode = ref<FlowNode>({
  id: '',
  key: '',
  title: '',
  icon: '',
  type: 'success',
  group: '',
  endpointOptions: [],
  stepBody: {},
  nextNodes: [],
  parentNodes: [],
  position: [0, 0]
});
// 条件节点
const conditionNode = ref<ConditionFlowNode>({
  id: '',
  nodeId: '',
  label: '',
  conditions: []
});

// tab值
const tabValue = ref<string>('node');
// 当前条件
const currentConnection = ref<any>(null);
// 临时步骤名称
const tempStepBodyName = ref<string>('');

// 步骤Bodys
const stepBodys = computed(() => {
  return workflowStore.getStepBodys;
});

// 创建节点类型
const createNodeByType = (type: string, key?: string) => {
  let node = JSON.parse(JSON.stringify(sourceNodes.value.filter((u) => u.type == type)[0]));
  node.key =
    key !== undefined ? key : node.key + '_' + Date.now() + Math.random().toString(36).substring(2);
  if (node.endpointOptions !== null) {
    node.endpointOptions.forEach((option: any) => {
      option.uuid = node.key + option.anchor;
    });
  }
  return node;
};

// 添加节点 type:节点类型
const addNodeByType = async (type: string) => {
  if (type === 'success' && flowNodeValue.value.filter((i) => i.type === type).length > 0) {
    ElMessage.error('一个流程只能有一个开始节点');
    return;
  }
  let node = createNodeByType(type);
  flowNodeValue.value.push(node);
  await nextTick(() => {
    addNode(node);
  });
};

//添加节点
const addNode = (node: FlowNode) => {
  plumbIns.value.draggable(node.key, {
    containment: 'jsplumb-container',
    drag: (params: DragEventCallbackOptions) => {
      currentNode.value.position = params.pos;
    }
  });
  let domNode: HTMLElement = document.getElementById(node.key)!;
  domNode.style.left = node.position![0] + 'px';
  domNode.style.top = node.position![1] + 'px';
  if (node.endpointOptions !== null) {
    node.endpointOptions.forEach((option: any) => {
      plumbIns.value.addEndpoint(node.key, option, workflowStore.getEndpointOptions);
    });
  }
};

// 重画节点
const revalidate = () => {
  const key = currentNode.value?.key;
  if (key) {
    plumbIns.value.revalidate(key);
  }
};

// 重新验证连接
const revalidateConnection = () => {
  if (currentConnection.value !== null) {
    currentConnection.value.getOverlay('label').setLabel(conditionNode.value.label);
  }
};

// 删除节点
const removeNode = () => {
  ElMessageBox.confirm('您确定删除吗', '提醒', {
    confirmButtonText: '删除',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    let i = 0;
    for (let index = 0; index < flowNodeValue.value.length; index++) {
      if (flowNodeValue.value[index].key == currentNode.value.key) {
        break;
      }
      i++;
    }
    flowNodeValue.value.forEach((node) => {
      node.nextNodes = node.nextNodes.filter((u) => u.nodeId != currentNode.value.key);
      node.parentNodes = node.parentNodes.filter((u) => u != currentNode.value.key);
    });
    plumbIns.value.remove(currentNode.value.key);
    flowNodeValue.value.splice(i, 1);
  });
};

// 步骤节点
const setNode = (step: FlowNode) => {
  tabValue.value = 'node';
  isclickLine.value = false;
  currentNode.value = step;
  tempStepBodyName.value = '';
  if (currentNode.value.stepBody && currentNode.value.stepBody.name) {
    let sourceBody = JSON.parse(
      JSON.stringify(stepBodys.value.filter((u) => u.name == currentNode.value.stepBody.name)[0])
    );
    currentNode.value.stepBody = {
      ...util.extend(true, sourceBody, currentNode.value.stepBody)
    };
    setTimeout(() => {
      tempStepBodyName.value = currentNode.value.stepBody.name;
    }, 10);
  }
};

// 添加条件
const addCondition = () => {
  if (conditionNode.value.conditions) {
    conditionNode.value.conditions.push({
      field: '',
      value: '',
      operator: '=='
    });
  }
};

// 删除条件
const removeCondition = (index: number) => {
  conditionNode.value.conditions.splice(index, 1);
};

// 删除连线
const removeConnection = () => {
  ElMessageBox.confirm('您确定删除吗', '提醒', {
    confirmButtonText: '删除',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    let source = getNode(currentConnection.value.sourceId);
    let target = getNode(currentConnection.value.targetId);
    source.nextNodes = source.nextNodes.filter((u) => u.nodeId == target.key);
    target.parentNodes = source.parentNodes.filter((u) => u != currentConnection.value.sourceId);
    plumbIns.value.deleteConnection(currentConnection.value);
  });
};

// 连线事件
const onConnection = (info: ConnectionMadeEventInfo) => {
  let sourse: FlowNode = flowNodeValue.value.filter((u) => u.key == info.sourceId)[0];
  let target: FlowNode = flowNodeValue.value.filter((u) => u.key == info.targetId)[0];
  if (target.parentNodes.filter((u) => u == sourse.key).length <= 0) {
    target.parentNodes.push(sourse.key);
  }
  if (sourse.nextNodes.filter((u) => u.nodeId == target.key).length <= 0) {
    let c: ConditionFlowNode = {
      id: '',
      nodeId: target.key,
      label: '',
      conditions: []
    };
    sourse.nextNodes.push(c);
  }
};

// 连线点击事件
const onClickConnection = (connection: Connection) => {
  const source: FlowNode = flowNodeValue.value.filter((u) => u.key == connection.sourceId)[0];
  const conditionFlowNode = source.nextNodes.filter((u) => u.nodeId == connection.targetId)[0];
  if (!conditionFlowNode.conditions) {
    conditionFlowNode.conditions = [];
  }
  conditionNode.value = conditionFlowNode;
  currentConnection.value = connection;
  isclickLine.value = true;
  tabValue.value = 'connection';
};

// 监听数据
watch(() => conditionNode.value, (newVal: ConditionFlowNode) => {
  for (let i = 0; i < flowNodeValue.value.length; i++) {
    if (flowNodeValue.value[i].key === currentConnection.value.sourceId) {
      for (let j = 0; j < flowNodeValue.value[i].nextNodes.length; j++) {
        if (flowNodeValue.value[i].nextNodes[j].nodeId === currentConnection.value.targetId) {
          flowNodeValue.value[i].nextNodes[j] = newVal;
          emits('update:modelValue', flowNodeValue.value);
        }
      }
    }
  }
}, { deep: true });

// 步骤操作选择
const onStepBodyChange = (value: string) => {
  currentNode.value.stepBody = JSON.parse(
    JSON.stringify(stepBodys.value.filter((u) => u.name == value)[0])
  );
};

// 初始化工作流数据
const initWorkflow = async () => {
  flowNodeValue.value = flowNodeValue.value.map((node) => {
    let snode = createNodeByType(node.type, node.key);
    return util.extend(snode, node);
  });
  await nextTick(() => {
    flowNodeValue.value.forEach((node) => {
      addNode(node);
    });
    let createConnect = (node: FlowNode) => {
      let i = node.parentNodes.length > 0 && node.endpointOptions.length > 1 ? 1 : 0;
      let nodeEndpoints = node.endpointOptions;
      node.nextNodes.forEach((nnode) => {
        let nnodeEndpoints = getNode(nnode.nodeId).endpointOptions;
        plumbIns.value.connect({
          uuids: [nodeEndpoints[nodeEndpoints.length - 1].uuid, nnodeEndpoints[0].uuid],
          label: nnode.label,
          endpoint: workflowStore.getEndpointOptions
        });
        i++;
        i = i >= nodeEndpoints.length ? 0 : i;
      });
    };
    flowNodeValue.value.forEach((node) => {
      createConnect(node);
    });
  });
};
</script>
<style scoped lang="scss">
.split-container {
  display: flex;

  .split-pane {
    .split-pane-card {
      min-height: $work_flow_min_height;
      margin: 0;
      padding: 0;

      .split-pane-tabs {
        .delet-btn {
          width: 100%;
          border: 1px solid rgb(239, 84, 44);
          color: rgb(239, 84, 44);
        }

        .delet-btn:hover {
          background-color: rgba(254, 245, 243, 0.5);
          color: #f16643;
        }
      }
    }
  }

  .jsplumb-container {
    position: relative;
    min-height: calc($work_flow_min_height - 42px);

    .wf-btn {
      position: absolute !important;
    }
  }

  //:deep深度选择器
  :deep(.gutter) {
    border: 1px solid #ccc;
    background-color: #eee;
    border-radius: 30px;
    display: flex;
    align-items: center;
    /* 垂直居中 */
    justify-content: center;
    /* 水平居中（如果需要） */
    flex-direction: column;
    /* 垂直排列 */

    &:hover {
      cursor: col-resize;
    }

    &::before,
    &::after {
      content: '';
      display: block;
      width: 8px;
      height: 6px;
      border-top: 1px solid #ccc;
      border-bottom: 1px solid #ccc;
      margin-top: 2px;
      margin-bottom: 2px;
    }
  }
}
</style>
